﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using GolfLeague.EditModels;
using GolfLeague.ViewModels;

namespace GolfLeague.Models
{
    public class ScoreCardInterface
    {
        GolfLeagueEntities golfDB = new GolfLeagueEntities();

        #region Hole Functions

        public IQueryable<Hole> GetAllCourseHoles(int courseID)
        {
            return from holeEntity in golfDB.Holes
                   where holeEntity.CourseID.Equals(courseID)
                   orderby holeEntity.HoleNumber
                   select holeEntity;
        }

        public IQueryable<short> GetCourseHoleNumbers(int courseID)
        {
            return from holeEntity in golfDB.Holes
                   where holeEntity.CourseID.Equals(courseID)
                   orderby holeEntity.HoleNumber
                   select holeEntity.HoleNumber;
        }

        public IQueryable<Hole> GetFrontCourseHoles(int courseID)
        {
            return from holeEntity in golfDB.Holes
                   where holeEntity.CourseID.Equals(courseID) && holeEntity.HoleNumber < 10
                   orderby holeEntity.HoleNumber
                   select holeEntity;
        }

        public IQueryable<Hole> GetBackCourseHoles(int courseID)
        {
            return from holeEntity in golfDB.Holes
                   where holeEntity.CourseID.Equals(courseID) && holeEntity.HoleNumber > 9
                   orderby holeEntity.HoleNumber
                   select holeEntity;
        }

        public Hole GetHole(int holeID)
        {
            var holeQuery = from holeEntity in golfDB.Holes
                            where holeEntity.HoleID.Equals(holeID)
                            select holeEntity;

            return holeQuery.FirstOrDefault();
        }

        public int GetHoleID(int courseID, int holeNumber)
        {
            var holeQuery = from holeEntity in golfDB.Holes
                            where holeEntity.CourseID.Equals(courseID) && holeEntity.HoleNumber.Equals(holeNumber)
                            select holeEntity.HoleID;

            return holeQuery.FirstOrDefault();
        }

        public bool AddHole(Hole hole)
        {
            if (!HoleExists(hole))
            {
                golfDB.Holes.AddObject(hole);
                return true;
            }
            else
            {
                return false;
            }
        }

        public void DeleteHole(Hole hole)
        {
            golfDB.Holes.DeleteObject(hole);
        }

        public bool HoleExists(Hole hole)
        {
            var holeQuery = from holeEntity in golfDB.Holes
                            where holeEntity.CourseID.Equals(hole.CourseID) && holeEntity.HoleNumber.Equals(hole.HoleNumber)
                            select holeEntity;

            return (holeQuery.Count() > 0 ? true : false);
        }

        #endregion

        #region Hole Detail Functions

        public HoleDetail GetHoleDetails(int holeID, int teeBoxID)
        {
            var holeDetailQuery = from teeBoxEntity in golfDB.HoleDetails
                                  where teeBoxEntity.HoleID.Equals(holeID) && teeBoxEntity.TeeBoxID.Equals(teeBoxID)
                                  select teeBoxEntity;

            return holeDetailQuery.FirstOrDefault();
        }

        public IQueryable<HoleDetail> GetAllHoleDetails(int holeID)
        {
            return from teeBoxEntity in golfDB.HoleDetails
                   where teeBoxEntity.HoleID.Equals(holeID)
                   orderby teeBoxEntity.TeeBox.TeeBoxName
                   select teeBoxEntity;
        }

        public IQueryable<HoleDetail> GetFrontHoleDetails(int teeBoxID)
        {
            return from teeBoxEntity in golfDB.HoleDetails
                   where teeBoxEntity.TeeBoxID.Equals(teeBoxID) && teeBoxEntity.Hole.HoleNumber < 10
                   orderby teeBoxEntity.Hole.HoleNumber
                   select teeBoxEntity;
        }

        public IQueryable<HoleDetail> GetBackHoleDetails(int teeBoxID)
        {
            return from holeDetailsEntity in golfDB.HoleDetails
                   where holeDetailsEntity.TeeBoxID.Equals(teeBoxID) && holeDetailsEntity.Hole.HoleNumber > 9
                   orderby holeDetailsEntity.Hole.HoleNumber
                   select holeDetailsEntity;
        }

        public bool AddHoleDetails(HoleDetail holeDetails)
        {
            if (!HoleDetailsExist(holeDetails))
            {
                golfDB.HoleDetails.AddObject(holeDetails);
                return true;
            }
            else
            {
                return false;
            }
        }

        public void DeleteHoleDetails(HoleDetail holeDetails)
        {
            golfDB.HoleDetails.DeleteObject(holeDetails);
        }

        public bool HoleHasHoleDetails(int holeID)
        {
            return (GetAllHoleDetails(holeID).Count() > 0 ? true : false);
        }

        public bool HoleDetailsExist(HoleDetail holeDetails)
        {
            var holeDetailsQuery = from holeDetailsEntity in golfDB.HoleDetails
                                   where holeDetailsEntity.HoleID.Equals(holeDetails.HoleID) && holeDetailsEntity.TeeBoxID.Equals(holeDetails.TeeBoxID)
                                   select holeDetailsEntity;
            return (holeDetailsQuery.Count() > 0 ? true : false);
        }

        #endregion

        #region TeeBox Functions

        public IQueryable<TeeBox> GetAllTeeBoxes()
        {
            return from teeBoxEntity in golfDB.TeeBoxes
                   orderby teeBoxEntity.Course.CourseName
                   select teeBoxEntity;
        }

        public IQueryable<TeeBox> GetAllCourseTeeBoxes(int courseID)
        {
            return from teeBoxEntity in golfDB.TeeBoxes
                   where teeBoxEntity.CourseID.Equals(courseID)
                   orderby teeBoxEntity.TeeBoxName
                   select teeBoxEntity;
        }

        public TeeBox GetTeeBox(int teeBoxID)
        {
            var teeBoxQuery = from teeBoxEntity in golfDB.TeeBoxes
                              where teeBoxEntity.TeeBoxID.Equals(teeBoxID)
                              select teeBoxEntity;

            return teeBoxQuery.FirstOrDefault();
        }

        public bool AddTeeBox(TeeBox teeBox)
        {
            if (!TeeBoxExists(teeBox))
            {
                golfDB.TeeBoxes.AddObject(teeBox);
                return true;
            }
            else
            {
                return false;
            }
        }

        public void DeleteTeeBox(TeeBox teeBox)
        {
            IEnumerable<HoleDetail> holeDetails = GetFrontHoleDetails(teeBox.TeeBoxID);

            foreach (HoleDetail teeBoxHoleDetails in holeDetails)
            {
                golfDB.HoleDetails.DeleteObject(teeBoxHoleDetails);
            }

            holeDetails = GetBackHoleDetails(teeBox.TeeBoxID);

            foreach (HoleDetail teeBoxHoleDetails in holeDetails)
            {
                golfDB.HoleDetails.DeleteObject(teeBoxHoleDetails);
            }

            golfDB.TeeBoxes.DeleteObject(teeBox);
        }

        public bool TeeBoxExists(TeeBox teeBox)
        {
            var teeBoxQuery = from teeBoxEntity in golfDB.TeeBoxes
                              where teeBoxEntity.CourseID.Equals(teeBox.CourseID) && teeBoxEntity.TeeBoxName.Equals(teeBox.TeeBoxName)
                              select teeBoxEntity;

            return (teeBoxQuery.Count() > 0 ? true : false);
        }

        #endregion

        public void Save()
        {
            golfDB.SaveChanges();
        }

        #region Edit/View Model Builders

        public CourseScoreCardEditModel GetCourseScoreCardEditModel(int courseID)
        {
            IEnumerable<StandardDropdown> teeBoxDropDown = BuildTeeBoxDropDown(courseID);
            

            return new CourseScoreCardEditModel
            {
                FrontHoles = GetFrontCourseHoles(courseID),
                BackHoles = GetBackCourseHoles(courseID),
                FrontHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Front),
                BackHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Back),
                CourseName = GetCourseName(courseID),
                NewHole = new Hole { CourseID = courseID },
                NewTeeBox = new TeeBox { CourseID = courseID },
                NewHoleDetails = new HoleDetail(),
                TeeBoxDropDown = teeBoxDropDown
            };
        }

        public ScoreCardEditModel GetScoreCardEditModel(int courseID, int teeBoxID, Match match, IEnumerable<PlayerMatch> playerMatches)
        {
            ScoreCardEditModel roundScoreCardsEditModel = new ScoreCardEditModel
            {
                FrontHoles = GetFrontCourseHoles(courseID),
                BackHoles = GetBackCourseHoles(courseID),
                FrontHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Front).Single(t => t.TeeBox.TeeBoxID == teeBoxID),
                BackHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Front).Single(t => t.TeeBox.TeeBoxID == teeBoxID),
                Match = match
            };
            List<MatchesHandicaps> matchesHandicaps = new List<MatchesHandicaps>();
            CourseSides courseSide = (CourseSides)match.Round.SideID;
            HandicapInterface handicapInterface = new HandicapInterface();
            foreach (PlayerMatch playerMatch in playerMatches)
            {
                if (matchesHandicaps.Where(o => o.OpponentMatch.PlayerID == playerMatch.PlayerID).Count() > 0)
                    continue;

                PlayerMatch opponentMatch = playerMatches.Single(m => m.PlayerID == playerMatch.OpponentID);

                int playerAHandicap = handicapInterface.GetCurrentHandicap(playerMatch.PlayerID).Handicap;
                int playerBHandicap = handicapInterface.GetCurrentHandicap(opponentMatch.PlayerID).Handicap;

                if (courseSide != CourseSides.Both)
                {
                    int frontPar = GetFrontHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                    int backPar = GetBackHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                    playerAHandicap = GetPlayerHandicap(playerAHandicap, frontPar, backPar, courseSide);
                    playerBHandicap = GetPlayerHandicap(playerBHandicap, frontPar, backPar, courseSide);
                }

                matchesHandicaps.Add(new MatchesHandicaps
                {
                    PlayerMatch = playerMatch,
                    OpponentMatch = opponentMatch,
                    HandicapStrokes = GetHandicapStrokes(courseSide, playerMatch).OrderBy(h => h.HoleNumber).ToList(),
                    PlayerAHandicap = playerAHandicap,
                    PlayerBHandicap = playerBHandicap
                });
            }
            roundScoreCardsEditModel.PlayerMatches = matchesHandicaps;

            return roundScoreCardsEditModel;
        }

        public ScoresScorecardViewModel GetScoresScorecardViewModel(int courseID, int teeBoxID)
        {
            return new ScoresScorecardViewModel
            {
                FrontHoles = GetFrontCourseHoles(courseID).ToList(),
                BackHoles = GetBackCourseHoles(courseID).ToList(),
                FrontHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Front).Single(t => t.TeeBox.TeeBoxID == teeBoxID),
                BackHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Front).Single(t => t.TeeBox.TeeBoxID == teeBoxID),
            };
        }

        public ScoreCardViewModel GetCourseScoreCardViewModel(int courseID)
        {
            return new ScoreCardViewModel
            {
                FrontHoles = GetFrontCourseHoles(courseID),
                BackHoles = GetBackCourseHoles(courseID),
                FrontHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Front),
                BackHoleDetails = GetCourseHoleDetails(courseID, CourseSides.Back),
                CourseName = GetCourseName(courseID)
            };
        }

        public RoundMatchesEditModel GetRoundMatchesEditModel(int? id)
        {
            MatchInterface matchInterface = new MatchInterface();
            RoundInterface roundInterface = new RoundInterface();
            ScheduleInterface scheduleInterface = new ScheduleInterface();

            IEnumerable<Schedule> schedules = scheduleInterface.GetAllSchedules();
            int scheduleID;
            int roundID = 0;
            List<RoundsDropDownList> roundsDropDownList = new List<RoundsDropDownList>();
            IEnumerable<Match> matches;

            if (id == null)
            {
                if (schedules.FirstOrDefault(s => s.ScheduleYear == (short)DateTime.Now.Year) != null)
                {
                    scheduleID = schedules.FirstOrDefault(s => s.ScheduleYear == (short)DateTime.Now.Year).ScheduleID;
                }
                else
                {
                    scheduleID = (schedules.Count() > 0 ? schedules.FirstOrDefault().ScheduleID : 0);
                }
            }
            else
            {
                roundID = Convert.ToInt32(id);
                Round round = roundInterface.GetRound(roundID);
                scheduleID = round.ScheduleID;
            }

            foreach (Round round in roundInterface.GetAllScheduleRounds(scheduleID))
            {
                roundsDropDownList.Add(new RoundsDropDownList
                {
                    RoundID = round.RoundID,
                    RoundDate = round.RoundDate.ToShortDateString()
                });
            }

            if (roundsDropDownList.Count > 0)
            {
                matches = matchInterface.GetRoundMatches(roundsDropDownList.FirstOrDefault().RoundID);
            }
            else
            {
                matches = matchInterface.GetRoundMatches(0);
                roundsDropDownList.Add(new RoundsDropDownList
                {
                    RoundID = 0,
                    RoundDate = "No rounds exist for this schedule."
                });
            }

            List<ScoreCardEditModel> roundScoreCardsEditModels = new List<ScoreCardEditModel>();

            IEnumerable<PlayerMatch> matchPlayers;
            foreach (Match match in matches)
            {
                matchPlayers = matchInterface.GetMatchPlayerMatches(match.MatchID);
                if (matchPlayers.Count() > 0)
                    roundScoreCardsEditModels.Add(GetScoreCardEditModel(match.Round.CourseID, match.Round.TeeBoxID, match, matchInterface.GetMatchPlayerMatches(match.MatchID)));
            }

            return new RoundMatchesEditModel
            {
                Schedules = schedules,
                RoundsDropDownList = roundsDropDownList,
                Matches = matchInterface.GetMatchViewModels(roundsDropDownList.FirstOrDefault().RoundID),
                ScheduleID = scheduleID,
                RoundID = roundID,
                MatchScoreCards = roundScoreCardsEditModels
            };

        }

        #endregion 

        #region Helpers

        public IEnumerable<StandardDropdown> BuildTeeBoxDropDown(int courseID)
        {
            List<StandardDropdown> teeBoxDropDown = new List<StandardDropdown>();

            foreach (TeeBox teeBox in GetAllCourseTeeBoxes(courseID))
            {
                teeBoxDropDown.Add(new StandardDropdown
                {
                    dropdownID = teeBox.TeeBoxID,
                    dropdownText = teeBox.TeeBoxName
                });
            }

            if (teeBoxDropDown.Count == 0) teeBoxDropDown.Add(new StandardDropdown { dropdownID = 0, dropdownText = "No TeeBoxes Found" });

            return teeBoxDropDown;
        }

        public IEnumerable<TeeBoxHoleDetails> GetCourseHoleDetails(int courseID, CourseSides courseSide)
        {
            List<TeeBoxHoleDetails> holeDetails = new List<TeeBoxHoleDetails>();
            IEnumerable<TeeBox> teeBoxes = GetAllCourseTeeBoxes(courseID);

            foreach (TeeBox teeBox in teeBoxes)
            {
                holeDetails.Add(new TeeBoxHoleDetails
                {
                    HoleDetail = (courseSide == CourseSides.Front ? GetFrontHoleDetails(teeBox.TeeBoxID) : GetBackHoleDetails(teeBox.TeeBoxID)),
                    TeeBox = teeBox
                });
            }

            return holeDetails;
        }

        public IEnumerable<HandicapHoleStrokes> GetHandicapStrokes(CourseSides courseSide, PlayerMatch playerMatch)
        {
            HandicapInterface handicapInterface = new HandicapInterface();
            MatchInterface matchInterface = new MatchInterface();
            PlayerMatch opponentMatch = matchInterface.GetMatchPlayerMatches(playerMatch.MatchID).SingleOrDefault(p => p.PlayerID == playerMatch.OpponentID);
            int playerAHandicap = handicapInterface.GetHandicap(playerMatch.HandicapID).Handicap;
            int playerBHandicap = (opponentMatch != null ? handicapInterface.GetHandicap(opponentMatch.HandicapID).Handicap : 0);
            int playerID = playerMatch.PlayerID;


            if (courseSide != CourseSides.Both)
            {
                int frontPar = GetFrontHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                int backPar = GetBackHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                playerAHandicap = GetPlayerHandicap(playerAHandicap, frontPar, backPar, courseSide);
                playerBHandicap = GetPlayerHandicap(playerBHandicap, frontPar, backPar, courseSide);
            }

            if (playerBHandicap > playerAHandicap)
                playerID = opponentMatch.PlayerID;

            return GetHandicapStrokesForHoles(courseSide, playerMatch.Match.Round.CourseID, (int)Math.Abs(playerAHandicap - playerBHandicap), playerID);

            //return GetHandicapStrokes(courseSide, playerMatch.Match.Round.CourseID, playerAHandicap, playerBHandicap);
        }

        //public IEnumerable<HandicapHoleStrokes> GetHandicapStrokes(CourseSides courseSide, int courseID, int playerAHandicap, int playerBHandicap)
        //{
        //    int strokeDiff = (int)Math.Abs(playerAHandicap - playerBHandicap);
        //    return GetHandicapStrokesForHoles(courseSide, courseID, strokeDiff);
        //    List<HandicapHoleStrokes> handicapStrokes = new List<HandicapHoleStrokes>(9);
        //    IEnumerable<Hole> holes = (courseSide == CourseSides.Front ? GetFrontCourseHoles(courseID).OrderBy(h => h.Handicap) : GetBackCourseHoles(courseID).OrderBy(h => h.Handicap));
        //    foreach (Hole hole in holes.OrderBy(h => h.HoleNumber))
        //        handicapStrokes.Add(new HandicapHoleStrokes
        //        {
        //            HoleNumber = hole.HoleNumber,
        //            Strokes = 0
        //        });

        //    while (strokeDiff > 0)
        //    {
        //        foreach (Hole hole in holes)
        //        {
        //            if (strokeDiff > 0)
        //            {
        //                handicapStrokes.Single(h => h.HoleNumber == hole.HoleNumber).Strokes++;
        //                strokeDiff--;
        //            }
        //            else
        //            {
        //                break;
        //            }
        //        }
        //    };

        //    return handicapStrokes;
        //}

        public IEnumerable<HandicapHoleStrokes> GetHandicapStrokesForNet(CourseSides courseSide, PlayerMatch playerMatch)
        {
            HandicapInterface handicapInterface = new HandicapInterface();
            int playerHandicap = handicapInterface.GetHandicap(playerMatch.HandicapID).Handicap;

            if (courseSide != CourseSides.Both)
            {
                int frontPar = GetFrontHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                int backPar = GetBackHoleDetails(playerMatch.Match.Round.TeeBoxID).Sum(h => h.Par);
                playerHandicap = GetPlayerHandicap(playerHandicap, frontPar, backPar, courseSide);
            }
            return GetHandicapStrokesForHoles(courseSide, playerMatch.Match.Round.CourseID, playerHandicap, playerMatch.PlayerID);
            //List<HandicapHoleStrokes> handicapStrokes = new List<HandicapHoleStrokes>(9);
            //IEnumerable<Hole> holes = (courseSide == CourseSides.Front ? GetFrontCourseHoles(playerMatch.Match.Round.CourseID).OrderBy(h => h.Handicap) : GetBackCourseHoles(playerMatch.Match.Round.CourseID).OrderBy(h => h.Handicap));
            //foreach (Hole hole in holes.OrderBy(h => h.HoleNumber))
            //    handicapStrokes.Add(new HandicapHoleStrokes
            //    {
            //        HoleNumber = hole.HoleNumber,
            //        Strokes = 0
            //    });

            //while (playerHandicap > 0)
            //{
            //    foreach (Hole hole in holes)
            //    {
            //        if (playerHandicap > 0)
            //        {
            //            handicapStrokes.Single(h => h.HoleNumber == hole.HoleNumber).Strokes++;
            //            playerHandicap--;
            //        }
            //        else
            //        {
            //            break;
            //        }
            //    }
            //};

            //return handicapStrokes;
        }

        private IEnumerable<HandicapHoleStrokes> GetHandicapStrokesForHoles(CourseSides courseSide, int courseID, int strokes, int playerID)
        {
            List<HandicapHoleStrokes> handicapStrokes = new List<HandicapHoleStrokes>(9);
            IEnumerable<Hole> holes = (courseSide == CourseSides.Front ? GetFrontCourseHoles(courseID).OrderBy(h => h.Handicap) : GetBackCourseHoles(courseID).OrderBy(h => h.Handicap));
            foreach (Hole hole in holes.OrderBy(h => h.HoleNumber))
                handicapStrokes.Add(new HandicapHoleStrokes
                {
                    HoleNumber = hole.HoleNumber,
                    Strokes = 0,
                    PlayerID = playerID
                });

            while (strokes > 0)
            {
                foreach (Hole hole in holes)
                {
                    if (strokes > 0)
                    {
                        handicapStrokes.Single(h => h.HoleNumber == hole.HoleNumber).Strokes++;
                        strokes--;
                    }
                    else
                    {
                        break;
                    }
                }
            };

            return handicapStrokes;
        }

        public int GetPlayerHandicap(int fullHandicap, int frontPar, int backPar, CourseSides side)
        {
            if ((fullHandicap % 2) == 0)
                fullHandicap /= 2;
            else
            {
                int frontHandicap = (frontPar > backPar ? fullHandicap / 2 + 1 : fullHandicap / 2);
                int backHandicap = (backPar > frontPar ? fullHandicap / 2 + 1 : fullHandicap / 2);
                if (frontPar == backPar) frontHandicap++;

                fullHandicap = (side == CourseSides.Front ? frontHandicap : backHandicap);
            }

            return fullHandicap;
        }

        public string GetCourseName(int courseID)
        {
            var courseQuery = from courseEntity in golfDB.Courses
                              where courseEntity.CourseID.Equals(courseID)
                              select courseEntity.CourseName;

            return courseQuery.FirstOrDefault();
        }

        #endregion

    }
}